/**
 * 
 */
package org.softTank.BpelTranslator.Impl;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.softTank.BcpModel.BcpActivity;
import org.softTank.BcpModel.BcpAssign;
import org.softTank.BcpModel.BcpControl;
import org.softTank.BcpModel.BcpControlType;
import org.softTank.BcpModel.BcpReceive;
import org.softTank.BpelModel.TActivity;
import org.softTank.BpelModel.TAssign;
import org.softTank.BpelModel.TCondition;
import org.softTank.BpelModel.TFlow;
import org.softTank.BpelModel.TLink;
import org.softTank.BpelModel.TLinks;
import org.softTank.BpelModel.TReceive;
import org.softTank.BpelModel.TSequence;
import org.softTank.BpelModel.TSource;
import org.softTank.BpelModel.TSources;
import org.softTank.BpelModel.TTarget;
import org.softTank.BpelModel.TTargets;
import org.softTank.BpelModel.TWhile;
import org.softTank.BpelTranslator.ActivityParser;
import org.softTank.BpelTranslator.AssignParser;
import org.softTank.BpelTranslator.BoolExpressionParser;
import org.softTank.BpelTranslator.ProcessParser;

/**
 * @author Yuzone
 * 
 */
public class ComplexProcessParserImpl implements ProcessParser {

	private ActivityParser activityParser;
	private AssignParser assignParser;
	private BoolExpressionParser exprParser;
	private List<BcpAssign> assignList;
	private Map<String, TActivity> activityMap;
	private Deque<BcpControl> ctrlQueue;
	private Set<BcpControl> ctrlSet;
	private Map<String, TSource> sourceMap;
	private int count = 1;
	private TAssign rootAssign;
	private TFlow flow;

	public ComplexProcessParserImpl(ActivityParser value1, AssignParser value2) {
		this.activityParser = value1;
		this.assignParser = value2;
		this.exprParser = new BoolExpressionParserImpl();
		activityMap = new HashMap<String, TActivity>();
		sourceMap = new HashMap<String, TSource>();
		ctrlQueue = new ArrayDeque<BcpControl>();
	}

	public List<BcpAssign> getAssignList() {
		if (assignList == null) {
			assignList = new ArrayList<BcpAssign>();
		}
		return assignList;
	}

	public Set<BcpControl> getCtrlSet() {
		if (ctrlSet == null) {
			ctrlSet = new HashSet<BcpControl>();
		}
		return ctrlSet;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.softTank.BpelTranslator.ProcessParser#parseProcess(org.softTank.BcpModel
	 * .BcpReceive)
	 */
	@Override
	public TActivity parseProcess(BcpReceive value) {
		TSequence sequence = new TSequence();
		rootAssign = new TAssign();
		flow = new TFlow();
		TLinks links = new TLinks();

		flow.setLinks(links);
		rootAssign.setName("assign");
		sequence.getActivity().add(rootAssign);
		sequence.getActivity().add(flow);

		// Parse the first <receive> activity.
		TReceive receive = (TReceive) activityParser.parseActivity(value);
		flow.getActivity().add(receive);
		activityMap.put(receive.getName(), receive);
		if (value.getInferiors().size() < 1
				|| !(value.getInferiors().get(0) instanceof BcpControl))
			return sequence;
		ctrlQueue.offer((BcpControl) value.getInferiors().get(0));

		// Parse the rest activities.
		while (!ctrlQueue.isEmpty()) {
			BcpControl bc = ctrlQueue.poll();
			if (!getCtrlSet().contains(bc)) {
				this.parseControl(bc);
				getCtrlSet().add(bc);
			}
		}
		this.updateActivity();

		return sequence;
	}

	private void parseControl(BcpControl value) {
		switch (value.getType()) {
		case BcpControlType.AND_JOIN:
			this.parseAndJoin(value);
			break;
		case BcpControlType.AND_SPLIT:
			this.parseAndSplit(value);
			break;
		case BcpControlType.SEQUENCE:
			this.parseSequence(value);
			break;
		case BcpControlType.LOOP:
			this.parseLoop(value);
			break;
		default:
		}
	}

	private void parseAndSplit(BcpControl value) {
		TAssign assign = assignParser.createAssign(value.getAssignList());
		rootAssign.getCopyOrExtensionAssignOperation().addAll(
				assignParser.createStructDefinition(value.getAssignList()));
		this.getAssignList().addAll(value.getAssignList());

		TTargets targets = new TTargets();
		assign.setTargets(targets);
		assign.setSources(new TSources());
		targets.getTarget().add(this.createTarget());
		sourceMap.put(value.getSuperiors().get(0).getName(), this
				.createSource());
		flow.getActivity().add(assign);

		TFlow f = new TFlow();
		targets = new TTargets();
		targets.getTarget().add(this.createTarget());
		f.setTargets(targets);
		assign.getSources().getSource().add(this.createSource());

		for (BcpActivity ba : value.getInferiors()) {
			TActivity activity = activityParser.parseActivity(ba);
			f.getActivity().add(activity);
			activityMap.put(activity.getName(), activity);
			this.offerCtrl(ba);
		}

		flow.getActivity().add(f);
	}

	private void parseAndJoin(BcpControl value) {
		TAssign assign = assignParser.createAssign(value.getAssignList());
		this.getAssignList().addAll(value.getAssignList());
		rootAssign.getCopyOrExtensionAssignOperation().addAll(
				assignParser.createStructDefinition(value.getAssignList()));

		TTargets targets = new TTargets();
		assign.setTargets(targets);
		TSources sources = new TSources();
		assign.setSources(sources);

		StringBuffer sb = new StringBuffer();
		for (BcpActivity ba : value.getSuperiors()) {
			TTarget t = this.createTarget();
			assign.getTargets().getTarget().add(t);
			if (sb.length() != 0) {
				sb.append(" and ");
			}
			sb.append("$").append(t.getLinkName());
			sourceMap.put(ba.getName(), this.createSource());
		}
		TCondition condition = new TCondition();
		condition.getContent().add(sb.toString());
		targets.setJoinCondition(condition);

		flow.getActivity().add(assign);

		TActivity activity = activityParser.parseActivity(value.getInferiors()
				.get(0));
		activityMap.put(activity.getName(), activity);
		TTargets ts = new TTargets();
		ts.getTarget().add(this.createTarget());
		activity.setTargets(ts);
		assign.getSources().getSource().add(this.createSource());

		this.offerCtrl(value.getInferiors().get(0));
		flow.getActivity().add(activity);
	}

	private void parseLoop(BcpControl value) {
		TAssign assign = assignParser.createAssign(value.getAssignList());
		this.getAssignList().addAll(value.getAssignList());
		rootAssign.getCopyOrExtensionAssignOperation().addAll(
				assignParser.createStructDefinition(value.getAssignList()));

		assign.setTargets(new TTargets());
		assign.setSources(new TSources());
		assign.getTargets().getTarget().add(this.createTarget());
		sourceMap.put(value.getSuperiors().get(0).getName(), this
				.createSource());
		flow.getActivity().add(assign);

		TWhile w = new TWhile();
		w.setName("while" + count);
		activityMap.put(w.getName(), w);
		w.setTargets(new TTargets());
		w.getTargets().getTarget().add(this.createTarget());
		assign.getSources().getSource().add(this.createSource());

		w.setCondition(exprParser.parseExpression(value.getExpression()));

		TSequence seq = new TSequence();
		w.setSequence(seq);
		TActivity activity = activityParser.parseActivity(value.getInferiors()
				.get(0));
		seq.getActivity().add(activity);
		value.getInferiors().get(0).setName(w.getName());

		TAssign assignInLoop = assignParser.createAssign(value
				.getLoopAssignList());
		this.getAssignList().addAll(value.getAssignList());
		rootAssign.getCopyOrExtensionAssignOperation().addAll(
				assignParser.createStructDefinition(value.getLoopAssignList()));
		seq.getActivity().add(assignInLoop);

		this.offerCtrl(value.getInferiors().get(0));
		flow.getActivity().add(w);
	}

	private void parseSequence(BcpControl value) {
		TSequence sequence = new TSequence();
		TTargets targets = new TTargets();
		sequence.setTargets(targets);
		targets.getTarget().add(this.createTarget());
		sourceMap.put(value.getSuperiors().get(0).getName(), this
				.createSource());

		this.createSequenceBody(sequence, value);

		flow.getActivity().add(sequence);
	}

	private void createSequenceBody(TSequence sequence, BcpControl value) {
		this.getAssignList().addAll(value.getAssignList());
		rootAssign.getCopyOrExtensionAssignOperation().addAll(
				assignParser.createStructDefinition(value.getAssignList()));
		TAssign assign = assignParser.createAssign(value.getAssignList());
		sequence.getActivity().add(assign);

		TActivity activity = activityParser.parseActivity(value.getInferiors()
				.get(0));
		sequence.getActivity().add(activity);
		activityMap.put(activity.getName(), activity);

		for (BcpActivity ba : value.getInferiors().get(0).getInferiors()) {
			if (ba instanceof BcpControl) {
				BcpControl ctrl = (BcpControl) ba;
				if (ctrl.getType() != BcpControlType.SEQUENCE) {
					ctrlQueue.offer(ctrl);
				} else {
					createSequenceBody(sequence, ctrl);
				}
			}
		}
	}

	private void offerCtrl(BcpActivity value) {
		for (BcpActivity ba : value.getInferiors()) {
			if (ba instanceof BcpControl) {
				ctrlQueue.offer((BcpControl) ba);
			}
		}
	}

	private TSource createSource() {
		TSource source = new TSource();
		source.setLinkName("link" + count);
		count++;

		return source;
	}

	private TTarget createTarget() {
		TLink link = new TLink();
		link.setName("link" + count);
		flow.getLinks().getLink().add(link);
		TTarget target = new TTarget();
		target.setLinkName(link.getName());

		return target;
	}

	private void updateActivity() {
		for (String key : sourceMap.keySet()) {
			TActivity activity = activityMap.get(key);
			if (activity != null) {
				if (activity.getSources() == null) {
					activity.setSources(new TSources());
				}
				activity.getSources().getSource().add(sourceMap.get(key));
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.softTank.BpelTranslator.StructExtractor#extractStruct(org.softTank
	 * .BcpModel.BcpReceive)
	 */
	@Override
	public List<BcpAssign> extractStruct(BcpReceive value) {
		if (assignList == null) {
			parseProcess(value);
		}
		return assignList;
	}

}
